home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d26 / asmtut4.arc / CHAP22-1.DOC < prev    next >
Text File  |  1990-08-10  |  31KB  |  693 lines

  1.  
  2.  
  3.  
  4.                                                                            229
  5.  
  6.                                  CHAPTER 22 - BCD NUMBERS
  7.  
  8.  
  9.              This chapter covers numbers which are in BCD format, both packed
  10.              and unpacked. You will probably never need to write any programs
  11.              on the 8086 that need these instructions, so you can either do
  12.              this chapter because it is the only time that you will run into
  13.              them, or you can skip the chapter because you will never see them
  14.              again. My advice would be to go through it anyways so you know
  15.              what the capabilities of the 8086 are. The programs in this
  16.              chapter are more advanced so will be more of a challenge to your
  17.              understanding.
  18.  
  19.              BCD stands for binary coded decimals. In their unpacked form,
  20.              each byte stands for a single decimal digit. If we take a number
  21.              like 831974 and put it in memory, the bytes will look like this:
  22.  
  23.                  08h
  24.                  03h
  25.                  01h
  26.                  09h
  27.                  07h
  28.                  04h
  29.  
  30.              With high memory at the top and low memory at the bottom. Notice
  31.              that the top number is not ASCII '8' (hex 38), but the number 8
  32.              (hex 08). The same holds true for all the bytes; they are not
  33.              ASCII characters, they are numbers.
  34.  
  35.              Why would we want to have numbers like this? They use up more
  36.              space (about 2 1/2 times as much), and the arithmetic operations
  37.              are slower (a multiplication on the 8086 can be several HUNDRED
  38.              times slower). They are not used for scientific operations. They
  39.              are used in business for billing. In a typical billing operation,
  40.              the number is entered from a terminal and stored. At the end of
  41.              the month, the number is printed on one line of the bill, and
  42.              then added to the total. If it were converted to an integer, it
  43.              would be necessary to do one conversion during data entry, then
  44.              another conversion during printing. This way, the only time it
  45.              will be converted is if it is used in some arithmetic. 
  46.  
  47.              One excuse for using BCD numbers is that they are more accurate.
  48.              It is true that they have no rounding errors, but neither do
  49.              integers. Integers and BCD numbers can have the same accuracy. 
  50.  
  51.              The typical form for BCD numbers is 18 digits. If you want to
  52.              convert an 18 digit number to a standard integer, it takes about
  53.              25 multiplications. That is a lot of multiplications to convert
  54.              one number. Similarly, it takes about 25 divisions to get a
  55.              number out of integer form back into a BCD number. This is a big
  56.              waste of time for a business situation. We keep the numbers in
  57.              decimal form and use them occasionally for arithmetic. 
  58.  
  59.              Actually, you would have to be crazy to use an 8086 for BCD
  60.  
  61.              ______________________
  62.  
  63.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  64.  
  65.  
  66.  
  67.  
  68.              The PC Assembler Tutor                                        230
  69.              ______________________
  70.  
  71.              numbers. If you have a PC and use BCD numbers habitually, an 8087
  72.              coprocessor will work on BCD numbers at lightning speed. An
  73.              investment of $175 will save you countless hours of grief. Also,
  74.              it is next to impossible to do BCD division on the 8086, but
  75.              takes no longer than normal to do BCD division on the 8087.
  76.  
  77.              That being said, just consider this chapter an academic exercise.
  78.              It will give you the information so if the question should arise
  79.              at a party, you will be able to say how BCD arithmetic works on
  80.              the 8086.
  81.  
  82.              All 8086 numbers have the least significant digit in low memory
  83.              and the most significant digit in high memory. This includes
  84.              packed and unpacked BCD numbers. In the unpacked form, each byte
  85.              represents a digit, and has a value from 0 to 9. Here are some
  86.              numbers and their unpacked BCD encoding (in hex):
  87.  
  88.                  3986149        27        961728          74610
  89.  
  90.                    03h          02h         09h            07h
  91.                    09h          07h         06h            04h
  92.                    08h                      01h            06h
  93.                    06h                      07h            01h
  94.                    01h                      02h            00h
  95.                    04h                      08h
  96.                    09h
  97.  
  98.              This wastes a lot of space. Someone early on noticed that the
  99.              upper half byte is completely unused. You can cut the space
  100.              consumption in half if you put two digits in each byte - one in
  101.              the lower half byte, and the other in the upper half byte. This
  102.              packing always starts from the least significant digit and goes
  103.              to the most significant digit. Here are the same numbers in
  104.              packed form.
  105.  
  106.                  3986149        27        961728          74610
  107.  
  108.                    03h          27h         96h            07h
  109.                    98h                      17h            46h
  110.                    61h                      28h            10h
  111.                    49h
  112.  
  113.              This is a considerable saving in space. The 8087 (not 8086)
  114.              standard for these numbers is 10 byte long numbers. The low order
  115.              9 bytes contain 18 digits. The last byte is 00h if the number is
  116.              positive and 80h if the number is negative. Here are an 18 digit
  117.              positive number and an 18 digit negative number, along with their
  118.              10 byte BCD encoding.
  119.  
  120.  
  121.  
  122.  
  123.  
  124.  
  125.  
  126.  
  127.  
  128.  
  129.  
  130.  
  131.  
  132.              Chapter 22 - BCD Numbers                                      231
  133.              ________________________
  134.  
  135.                       +137486298374691552           -581726405829645298
  136.  
  137.                            00                            80
  138.                            13                            58
  139.                            74                            17
  140.                            86                            26
  141.                            29                            40
  142.                            83                            58
  143.                            74                            29
  144.                            69                            64
  145.                            15                            52
  146.                            52                            98
  147.  
  148.  
  149.              Let's work with the packed BCD numbers first.
  150.  
  151.  
  152.              PACKED BCD NUMBERS
  153.  
  154.              The 8086 can manipulate packed BCD numbers. There is a subprogram
  155.              in asmhelp.obj that gets a BCD number from the keyboard and one
  156.              that prints a BCD number on the screen. Let's do some input and
  157.              output first.
  158.  
  159.              ; - - - - - - - - - - START DATA BELOW THIS LINE
  160.              variable1dw   ?    ; first four bcd digits
  161.              variable2dw   ?    ; second four bcd digits
  162.              variable3dw   ?    ; third four bcd digits
  163.              variable4dw   ?    ; fourth four bcd digits
  164.              variable5dw   ?    ; last two bcd digits and sign
  165.              ; - - - - - - - - - - END DATA ABOVE THIS LINE
  166.  
  167.              ; - - - - - - - - - - START CODE BELOW THIS LINE
  168.  
  169.              outer_loop:
  170.                  lea  ax, variable1
  171.                  call get_bcd
  172.  
  173.                  mov  ax, variable5
  174.                  call print_hex
  175.                  mov  ax, variable4
  176.                  call print_hex
  177.                  mov  ax, variable3
  178.                  call print_hex
  179.                  mov  ax, variable2
  180.                  call print_hex
  181.                  mov  ax, variable1
  182.                  call print_hex
  183.  
  184.                  lea  ax, variable1
  185.                  call print_bcd
  186.                  loop outer_loop
  187.  
  188.              ; - - - - - - - - - - END CODE ABOVE THIS LINE
  189.  
  190.              This gets a 10 byte BCD number, prints it out in hex (with high
  191.              memory on top), and then prints the BCD number out again. Commas
  192.  
  193.  
  194.  
  195.  
  196.              The PC Assembler Tutor                                        232
  197.              ______________________
  198.  
  199.              are allowed. Printing the number in hex form allows you to see
  200.              what the numbers look like internally.
  201.  
  202.              There are two instructions for BCD numbers - DAA (decimal adjust
  203.              for addition), and DAS (decimal adjust for subtraction). We'll
  204.              use each one on individual bytes to see how they work. Let's try
  205.              DAA.
  206.  
  207.  
  208.              ; - - - - - - - - - - START CODE BELOW THIS LINE
  209.                  mov  ax_byte, 0C4h  ; half registers, hex
  210.                  mov  bx_byte, 094h  ; half regs signed, hex
  211.                  mov  dx_byte, 91h   ; half registers, signed
  212.                  lea  ax, ax_byte
  213.                  call set_reg_style
  214.  
  215.                  sub  cx, cx         ; clear cx for clarity
  216.              outer_loop:
  217.                  mov  ax, 0               ; clear the registers
  218.                  mov  bx, 0
  219.                  mov  dx, 0
  220.                  call set_count
  221.                  call show_regs
  222.  
  223.                  call get_hex_byte        ; byte for al
  224.                  mov  dx, ax              ; copy to dx
  225.                  push ax                  ; save al 
  226.                  call get_hex_byte        ; byte for bl
  227.                  mov  bl, al
  228.                  mov  bh, bl              ; copy to bh
  229.                  pop  ax                  ; restore al
  230.                  call show_regs_and_wait
  231.                  add  al, bl              ; normal add 
  232.                  mov  dx, ax              ; copy to dx
  233.                  call show_regs_and_wait
  234.                  daa                      ; make adjustment
  235.                  mov  dx, ax              ; copy to dx
  236.                  call show_regs_and_wait
  237.                  jmp  outer_loop
  238.  
  239.              ; - - - - - - - - - - END CODE ABOVE THIS LINE
  240.  
  241.              Since the program works with both BCD adjustments and integer
  242.              arithmetic, DX shows a signed integer copy of AX and BH shows a
  243.              signed integer copy of BL. A copy of AX is moved to DX every time
  244.              an operation is performed on AL.
  245.  
  246.              The idea of this subprogram is to enter hex numbers that look
  247.              like decimal numbers - e.g. 65h, 78h, 08h, 29h. You can enter
  248.              numbers that contain A-F if you want to see what happens with bad
  249.              data. Each half byte of a BCD number should look like a decimal
  250.              number. You will notice that the actual addition is done by ADD.
  251.              The alteration is done by DAA, which assumes that you have just
  252.              added two legitimate BCD numbers, and the result is in AL. The
  253.              register MUST be AL. What DAA does will be discussed in a moment.
  254.  
  255.  
  256.  
  257.  
  258.  
  259.  
  260.              Chapter 22 - BCD Numbers                                      233
  261.              ________________________
  262.  
  263.              The DAA instruction looks at AL as being two half bytes. If the
  264.              result from the lower half byte addition is 10 or over, the 8086
  265.              subtracts 10 and then adds 1 to the upper half byte.{1}  For
  266.              instance, if the result is 17, it subtracts 10, to leave 7 in the
  267.              lower half byte, and adds 1 to the upper half byte. After it has
  268.              done this, it looks at the upper half byte. If the upper half
  269.              byte is now 10 or over, it subtracts 10 and sets the carry flag
  270.              for future additions.{2}
  271.  
  272.              While the whole thing sounds a little confusing, if you look at
  273.              the bytes in hex, they will act in the same way as if you were
  274.              doing normal decimal addition with pencil and paper except all
  275.              the carries are done at one time. CF will be set if the hundreds
  276.              digit is 1 and will be cleared if the hundreds digit is 0. Use
  277.              the previous program a few times to get the hang of what is going
  278.              on. When you feel confident, we'll move on to subtraction.
  279.  
  280.  
  281.              SUBTRACTION
  282.  
  283.              DAS (decimal adjust for subtraction) is similar to DAA. It makes
  284.              an adjustment after the subtraction itself. We'll use the same
  285.              program as before, making two alterations. Where you have ADD,
  286.              change it to SUB ; where you have DAA, change it to DAS. That's
  287.              all.
  288.  
  289.                  add  ->   sub
  290.                  daa  ->   das
  291.  
  292.              Run the program, and put in a number of examples. If the lower
  293.              number is larger than the top number, there will be a borrow. 
  294.  
  295.              DAS works the opposite of DAA. If there has been a borrow into
  296.              the low half byte, it adds 10 to the low half byte and subtracts
  297.              1 from the high half byte. It then looks at the high half byte.
  298.              ____________________
  299.  
  300.                 1. The technical description is a little confusing, so if you
  301.              get muddled up, just forget it. Here it goes. (AF is the
  302.              auxilillary flag). The 8086 checks for either (1) the low half
  303.              byte > 9 (that is, between 10 and 15) or (2) AF = 1. AF is set on
  304.              a byte addition when there is a carry out of the low half byte,
  305.              that is, the result is greater than 16 for the low half byte. If
  306.              either of these events has occured, the 8086 ADDS 6 to the low
  307.              half byte. Why?  Let 10 + x represent the result of the addition,
  308.              where x < 10. We then have:
  309.  
  310.                  10 + x + 6 = 10 + 6 + x = 16 + x
  311.  
  312.              but 16 is 0001 0000 (10h), the first bit in the high byte, so
  313.              this has the effect of leaving the part less than 10 in the low
  314.              half byte and adding 1 (the carry) to the high half byte.
  315.  
  316.                 2. This is exactly the same logic as in the last footnote,
  317.              except that the 8086 adds 60h to the high byte. This has the
  318.              effect of shifting the excess out of AL altogether. The 8086 then
  319.              sets the carry flag. 
  320.  
  321.  
  322.  
  323.  
  324.              The PC Assembler Tutor                                        234
  325.              ______________________
  326.  
  327.              If there has been a borrow into it, the 8086 adds 10 to the high
  328.              half byte and sets the carry flag. 
  329.  
  330.              Do a few more examples with the program to make sure you
  331.              understand what's happening. It will look just like what you do
  332.              with pencil and paper, except that with pencil and paper you do
  333.              the borrow before you do the subtraction and here the borrow is
  334.              done afterwards if it is needed.
  335.  
  336.  
  337.              ADDITION AND SUBTRACTION
  338.  
  339.              We have a number of possibilities with addition and subtraction.
  340.              Here they are. The sign of the numbers is in parentheses and the
  341.              operation is in between.
  342.  
  343.                  (+) + (+)      (+) + (-)      (+) - (+)      (+) - (-)
  344.                  (-) + (+)      (-) + (-)      (-) - (+)      (-) - (-)
  345.  
  346.              The subroutines we use are going to assume that (1) both numbers
  347.              are the same sign, and (2) for subtraction, the larger number is
  348.              on top and the smaller number is on the bottom. There should be a
  349.              section of the program that decides whether addition or
  350.              subtraction should be used, and which number is on top. Then
  351.              comes the addition or subtraction. We will write the first part
  352.              later. For now, when you input numbers, both numbers must have
  353.              the same sign, and for subtraction, the larger number must be
  354.              first. Here's the addition subroutine. 
  355.  
  356.              ; - - - - - START SUBROUTINE BELOW THIS LINE 
  357.              _bcd_addition proc near
  358.  
  359.                  RESULT_ADDRESS      EQU  [bp + 8]
  360.                  BOTTOM_ADDRESS      EQU  [bp + 6]
  361.                  TOP_ADDRESS         EQU  [bp + 4]
  362.  
  363.                  push bp
  364.                  mov  bp, sp
  365.                  PUSHREGS  ax, bx, cx, si, di
  366.  
  367.                  mov  si, TOP_ADDRESS
  368.                  mov  bx, BOTTOM_ADDRESS
  369.                  mov  di, RESULT_ADDRESS
  370.                  mov  cx, 9          ; 9 bytes of BCD numbers
  371.                  clc                 ; clear the carry flag
  372.  
  373.              add_loop:
  374.                  mov  al, [si]       ; move and add bytes
  375.                  adc  al, [bx]       ; add two numbers and the carry
  376.                  daa                 ; bcd adjust
  377.                  mov  [di], al       ; store result
  378.                  inc  si             ; increment pointers
  379.                  inc  bx
  380.                  inc  di
  381.                  loop add_loop
  382.  
  383.                  jnc  continue_addition   ; BCD overflow if CF = 1
  384.  
  385.  
  386.  
  387.  
  388.              Chapter 22 - BCD Numbers                                      235
  389.              ________________________
  390.  
  391.                  lea  ax, overflow_message
  392.                  call print_string
  393.  
  394.              continue_addition:
  395.                  mov  al, [si]       ; sign of top addend to result
  396.                  mov  [di], al
  397.  
  398.                  POPREGS   ax, bx, cx, si, di
  399.                  pop  bp
  400.                  ret
  401.  
  402.              _bcd_addition endp
  403.              ; - - - - END SUBROUTINE ABOVE THIS LINE
  404.  
  405.              AL contains the first number.  We use the carry flag (ADC) for
  406.              carrying from byte to byte. The carry flag is cleared before the
  407.              first addition since we don't want a carry there. The INC
  408.              instruction was designed by Intel so that it would not alter the
  409.              carry flag just so we could use it in situations like this.
  410.  
  411.              If CF = 1 upon exiting the loop, the result was too large and we
  412.              had overflow. In this case we print a message to that effect.
  413.  
  414.              The result [di] can be stored in the same place as one of the
  415.              numbers. The addition of each byte is completed before the result
  416.              for that byte is stored, so this won't interfere with the
  417.              addition. We assume that the sign of the result is the sign of
  418.              the top addend. (If this goes into a working program, it will
  419.              only be used if both numbers have the same sign).
  420.  
  421.              This is designed as a C subroutine. The calling program pushes
  422.              things on the stack and it has the responsibility of popping them
  423.              off the stack on return from the call.
  424.  
  425.              Here's the calling routine:
  426.  
  427.              ; - - - - - ENTER DATA BELOW THIS LINE
  428.              bcd_num1 dt   ?
  429.              bcd_num2 dt   ?
  430.              bcd_num3 dt   ?
  431.              overflow_message   db  "We had overflow.", 0
  432.              ; - - - - - ENTER DATA ABOVE THIS LINE
  433.              ; - - - - - ENTER CODE BELOW THIS LINE
  434.  
  435.              outer_loop:
  436.                  lea  ax, bcd_num1        ; get 2 numbers for addition
  437.                  call get_bcd
  438.                  lea  ax, bcd_num2
  439.                  call get_bcd
  440.  
  441.                  lea  ax, bcd_num3        ; push addresses for subroutine
  442.                  push ax
  443.                  lea  ax, bcd_num2
  444.                  push ax
  445.                  lea  ax, bcd_num1
  446.                  push ax
  447.  
  448.  
  449.  
  450.  
  451.  
  452.              The PC Assembler Tutor                                        236
  453.              ______________________
  454.  
  455.                  call _bcd_addition 
  456.                  add  sp, 6                   ; 3 pushes = 6 bytes
  457.  
  458.                  lea  ax, bcd_num1        ; print both numbers and result
  459.                  call print_bcd
  460.                  lea  ax, bcd_num2
  461.                  call print_bcd
  462.                  lea  ax, bcd_num3
  463.                  call print_bcd
  464.                  loop outer_loop
  465.  
  466.              ; - - - - - ENTER CODE ABOVE THIS LINE
  467.  
  468.              This is the main program. The other one should be in the
  469.              subroutine section. This program is straightforward. We get two
  470.              numbers, call the subroutine, and print the numbers and the
  471.              result. Try some numbers. The results you get will always have
  472.              the sign of the top number and will have the same numerical
  473.              result as adding two unsigned numbers.
  474.  
  475.              The subtraction routine is the same as the addition but we need
  476.              to make some changes because it is subtraction:
  477.  
  478.                  ADC  ->   SBB
  479.                  DAA  ->   DAS  (decimal adjust for subtraction)
  480.  
  481.                  add_loop:      ->   subtract_loop
  482.                  loop add_loop  ->   loop subtract_loop
  483.  
  484.                  jmp  continue_addition   ->   jmp  continue_subtraction
  485.                  continue_addition:       ->   continue_subtraction:
  486.  
  487.              Also, we want to change the subroutine name and call
  488.  
  489.                  _bcd_addition       ->   _bcd_subtraction
  490.                  call _bcd_addition  ->   call _bcd_subtraction
  491.  
  492.              Do all these changes, run the program, but make sure the top
  493.              number is larger or you will get strange results.
  494.  
  495.              We now have an addition routine that only works with the right
  496.              numbers and a subtraction routine that is very touchy about the
  497.              numbers that are put in. How can these things help us? In fact
  498.              they are almost all we need. The only thing else we need is a
  499.              preliminary subroutine to organize what we do. To see why we have
  500.              some orginizational problems, take out a pencil and paper and do
  501.              the following additions and subtractions. Do these with a pencil
  502.              and paper, not a pocket calculator:
  503.  
  504.                  (+15)     +    (+27)               (+15)     +    (-27)
  505.                  (+15)     -    (+27)               (+15)     -    (-27)
  506.                  (-15)     +    (+27)               (-15)     +    (-27)
  507.                  (-15)     -    (+27)               (-15)     -    (-27)
  508.                  (+27)     +    (+15)               (+27)     +    (-15)
  509.                  (+27)     -    (+15)               (+27)     -    (-15)
  510.                  (-27)     +    (+15)               (-27)     +    (-15)
  511.                  (-27)     -    (+15)               (-27)     -    (-15)
  512.  
  513.  
  514.  
  515.  
  516.              Chapter 22 - BCD Numbers                                      237
  517.              ________________________
  518.  
  519.  
  520.              There are only four possible answers:  +12, -12, +42 and -42. We
  521.              had 16 different additions and subtractions, yet we got only 4
  522.              possible answers and only 2 possible absolute values. We could
  523.              have a different subroutine for each one, but 16 subprograms is a
  524.              LOT of code, so it's easier to do it with 2 subprograms. We
  525.              merely need to order the numbers and pick the correct subroutine.
  526.  
  527.              Here is the BCD driving routine. We'll get a number and then
  528.              we'll get an operation, either addition or subtraction. If it is
  529.              subtraction, when we get the second number, we REVERSE the sign.
  530.              We don't even check what it is; plus becomes minus and minus
  531.              becomes plus. What we have now is the ADDITION of two signed
  532.              numbers. We XOR the two signs. If the result is 0, they both are
  533.              the same sign and we can go to the addition subroutine. If the
  534.              signs are different we need to subtract the smaller from the
  535.              larger. We find out which one is larger and then call the
  536.              subtraction routine. It is going to take you a little time to
  537.              read this code.
  538.  
  539.              ; - - - - - START DATA BELOW THIS LINE
  540.              top_number    dt   ?
  541.              bottom_number dt   ?
  542.              result        dt   ?
  543.              sign_mask     db   ?
  544.              sign_message       db   "Enter either + or -.", 0
  545.              overflow_message   db   "We had overflow.", 0
  546.              ; - - - - - END DATA ABOVE THIS LINE
  547.  
  548.              ; - - - - - START CODE BELOW THIS LINE
  549.              outer_loop:
  550.                  lea  ax, top_number
  551.                  call get_bcd
  552.  
  553.              sign_loop:
  554.                  ; get either a '+' or a '-'
  555.                  lea  ax, sign_message    ; prompt for operation
  556.                  call print_string
  557.                  call get_ascii_byte      ; operation type in al
  558.                  cmp  al, '+'
  559.                  jne  check_for_minus
  560.                  mov  sign_mask, 00h      ; for XOR of bottom number sign
  561.                  jmp  get_second_number
  562.              check_for_minus:
  563.                  cmp  al, '-'
  564.                  jne  sign_loop           ; if not a minus then redo
  565.                  mov  sign_mask, 80h      ; for XOR of bottom sign
  566.  
  567.              get_second_number:
  568.                  lea  ax, bottom_number
  569.                  call get_bcd
  570.                  ; XOR bottom sign with sign mask
  571.                  mov  al, sign_mask
  572.                  xor  BYTE PTR (bottom_number + 9), al   ; sign byte
  573.  
  574.                  ; same sign or different signs?
  575.                  mov  ah, BYTE PTR (top_number + 9)      ; sign byte
  576.  
  577.  
  578.  
  579.  
  580.              The PC Assembler Tutor                                        238
  581.              ______________________
  582.  
  583.                  xor  ah, BYTE PTR (bottom_number + 9)   ; different?
  584.                  jnz  which_is_larger          ; if different, subtract
  585.  
  586.                  ; same sign, so add
  587.                  lea  ax, result               ; push parameters and add
  588.                  push ax
  589.                  lea  ax, bottom_number
  590.                  push ax
  591.                  lea  ax, top_number
  592.                  push ax
  593.                  call _bcd_addition
  594.                  add  sp, 6                    ; adjust the stack
  595.                  jmp  print_the_numbers
  596.  
  597.              which_is_larger:
  598.                  lea  si, top_number + 8       ; top digits
  599.                  lea  di, bottom_number + 8
  600.                  mov  cx, 9                    ; 9 bytes of digits
  601.              check_for_greater_loop:
  602.                  mov  al, [si]                 ; top number
  603.                  cmp  al, [di]                 ; bottom number
  604.                  ja   top_is_more
  605.                  jb   bottom_is_more
  606.                  dec  si                       ; equal, so continue
  607.                  dec  di
  608.                  loop check_for_greater_loop
  609.  
  610.                  ; we fell through so they are the same
  611.                  ; leave the top number on top
  612.              top_is_more:
  613.                  lea  ax, result
  614.                  push ax
  615.                  lea  ax, bottom_number
  616.                  push ax
  617.                  lea  ax, top_number
  618.                  push ax
  619.                  call _bcd_subtraction
  620.                  add  sp, 6                    ; adjust the stack
  621.                  jmp  print_the_numbers
  622.  
  623.              bottom_is_more:
  624.                  lea  ax, result
  625.                  push ax
  626.                  lea  ax, top_number
  627.                  push ax
  628.                  lea  ax, bottom_number
  629.                  push ax
  630.                  call _bcd_subtraction
  631.                  add  sp, 6                    ; adjust the stack
  632.  
  633.              print_the_numbers:
  634.                  lea  ax, top_number
  635.                  call print_bcd
  636.                  lea  ax, bottom_number
  637.                  call print_bcd
  638.                  lea  ax, result
  639.                  call print_bcd
  640.  
  641.  
  642.  
  643.  
  644.              Chapter 22 - BCD Numbers                                      239
  645.              ________________________
  646.  
  647.                  jmp  outer_loop
  648.              ; - - - - - END CODE ABOVE THIS LINE
  649.  
  650.              The sign_mask is either 00h if it is addition or 80h if it is
  651.              subtraction. 00h XOR sign_byte will leave the sign byte
  652.              unchanged. 80h XOR sign_byte will reverse the sign of the sign
  653.              byte.
  654.  
  655.              Thus, as we did in the earlier multiplication and division
  656.              routines, we sometimes change the sign of a number
  657.              (bottom_number). In a real routine we would either have to make a
  658.              copy of it when we change the sign or change the sign back at the
  659.              end. Here we leave it with the sign change (if any) so you can
  660.              see what is happening internally. If you want to restore the
  661.              number, you can alter the code a little:
  662.  
  663.                  print_the_numbers:
  664.                       mov  al, sign_mask                      ; add this
  665.                       xor  BYTE PTR (bottom_number + 9), al   ; add this
  666.                       lea  ax, top_number
  667.  
  668.              This will restore the bottom number to its original form ASSUMING
  669.              THAT THE RESULT HAS NOT BEEN STORED THERE.
  670.  
  671.              It is possible to get -0 as a result. This is a legally defined
  672.              number: +0 = -0.
  673.  
  674.              There are three places where we have 'BYTE PTR'. This is because
  675.              the variables are defined as 10 byte long objects and the
  676.              assembler will complain if we don't put in the 'BYTE PTR'.
  677.  
  678.              Finally, if we want to use the typical 'destination, source'
  679.              style for the 8086, it is built in. Here it is for addition:
  680.  
  681.                  lea  ax, top_number
  682.                  push ax
  683.                  lea  ax, bottom_number
  684.                  push ax
  685.                  lea  ax, top_number
  686.                  push ax
  687.  
  688.              We put the address of 'top_number' where the result address goes.
  689.              The routines store a byte only after all calculations are done on
  690.              that particular byte, so there is no interference from doing
  691.              this. 
  692.  
  693.